package based

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	cryptoRand "crypto/rand"
	"encoding/binary"
	"fmt"
	"github.com/google/uuid"
	//bswabe2 "github.com/zrynuaa/cpabe06/bswabe"
	//bswabe1 "github.com/zrynuaa/cpabe06_client/bswabe"
	"hash"
	"io"
	mathRand "math/rand"
	"net/http"
	"os"
)

func generateRandomKey(length int) ([]byte, error) {
	key := make([]byte, length)

	_, err := mathRand.Read(key)
	if err != nil {
		return nil, err
	}

	return key, nil
}

// 获取ABE服务器上的主公钥
//func GetABEKeys(attrs string) (*bswabe1.BswabePub, *bswabe1.BswabePrv) {
//
//	//    client, err := rpc.DialHTTP("tcp", "127.0.0.1")
//	//    if err != nil {
//	// 	   log.Fatal("dialing:", err)
//	//    }
//
//	//    // Synchronous call同步方式调用
//	//    var reply []byte
//	//    err = client.Call("CPABE.Getpub", "", &reply)
//	//    if err != nil {
//	// 	   log.Fatal(" error:", err)
//	//    }
//	//    var pub = bswabe.UnSerializeBswabePub(reply)//获取PublicKey
//
//	//    err = client.Call("CPABE.Getsk", attrs, &reply)
//	//    if err != nil {
//	// 	   log.Fatal("arith error:", err)
//	//    }
//	//    var prv = bswabe.UnSerializeBswabePrv(pub, reply) //获取服务端返回的解密私钥
//
//	pub := new(bswabe2.BswabePub)
//	msk := new(bswabe2.BswabeMsk)
//
//	data := bswabe2.SerializeBswabePub(pub)
//	ppuu := bswabe1.UnSerializeBswabePub(data) //获得服务端返回的公共参数
//
//	prv1 := bswabe2.CP_Keygen(pub, msk, attrs)
//	data = bswabe2.SerializeBswabePrv(prv1)
//	pprr := bswabe1.UnSerializeBswabePrv(ppuu, data) //获取服务端返回的解密私钥
//
//	return ppuu, pprr
//}

// 用户初始化
//
//	func Init(name string, pub *bswabe.BswabePub, prv *bswabe.BswabePrv) error{
//		if name == "" {
//			return fmt.Errorf("name is nothing")
//		}
//		Name := name
//		sk := prv
//		pk := pub
//		return nil
//	}
//
// HKDF
func hkdfExtract(salt, inputKeyMaterial []byte, hashFunc func() hash.Hash) []byte {
	hashLen := hashFunc().Size()

	if salt == nil || len(salt) == 0 {
		salt = make([]byte, hashLen)
	}

	h := hmac.New(hashFunc, salt)
	h.Write(inputKeyMaterial)
	prk := h.Sum(nil)

	return prk
}

func hkdfExpand(pseudoRandomKey, info []byte, length int, hashFunc func() hash.Hash) []byte {
	hashLen := hashFunc().Size()
	length = length

	// 计算需要的块数
	blocksNeeded := (length + hashLen - 1) / hashLen
	okm := make([]byte, 0)
	outputBlock := make([]byte, 0)

	for counter := 0; counter < blocksNeeded; counter++ {
		// 计算每个块的输出
		h := hmac.New(hashFunc, pseudoRandomKey)
		h.Write(outputBlock)
		h.Write(info)
		counterBytes := make([]byte, 4)
		binary.BigEndian.PutUint32(counterBytes, uint32(counter+1))
		h.Write(counterBytes)
		outputBlock = h.Sum(nil)

		// 将输出块追加到最终输出
		okm = append(okm, outputBlock...)
	}

	return okm[:length]
}

type Hkdf struct {
	prk []byte
}

func NewHkdf(salt, inputKeyMaterial []byte, hashFunc func() hash.Hash) *Hkdf {
	prk := hkdfExtract(salt, inputKeyMaterial, hashFunc)

	return &Hkdf{prk: prk}
}

func (h *Hkdf) Expand(info []byte, length int, hashFunc func() hash.Hash) []byte {
	return hkdfExpand(h.prk, info, length, hashFunc)
}

// AES加密
func encrypt(plaintext []byte, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	// 创建GCM（Galois/Counter Mode）模式
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	// 生成随机的nonce（一次性使用的值）
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(cryptoRand.Reader, nonce); err != nil {
		return nil, err
	}

	ciphertext := gcm.Seal(nil, nonce, plaintext, nil)
	result := append(nonce, ciphertext...)

	return result, nil
}

// AES解密
func decrypt(ciphertext []byte, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return nil, fmt.Errorf("ciphertext is too short")
	}
	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]

	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return nil, err
	}

	return plaintext, nil
}

func generateUUID() string {
	newUUID, err := uuid.NewRandom()
	if err != nil {
		fmt.Println("Error generating UUID:", err)
		return ""
	}
	return newUUID.String()
}

func AddHandletoServer(server *http.ServeMux, filename string) {
	fss := http.FileServer(http.Dir(os.Getenv("GOPATH") + "/src/github.com/zrynuaa/medicine_blockchain/frontend/static"))
	fsh := http.FileServer(http.Dir(os.Getenv("GOPATH") + "/src/github.com/zrynuaa/medicine_blockchain/frontend/html"))
	server.Handle("/static/", http.StripPrefix("/static/", fss))
	server.Handle("/html/"+filename, http.StripPrefix("/html/", fsh))
}
